// $Id: loggerdotnetworkthruput.cpp 73 2004-12-30 15:30:09Z hansemil $
#include "loggerdotnetworkthruput.h"
#include "loghandler.h"
#include "filehandler.h"
#include "file.h"
#include "node.h"
#include "distributions.h"
#include <iostream>
#include <iomanip>
#include <list>
#include <vector>
using namespace std;

/**
 * @todo Write documentation
 */
LoggerDotNetworkThruput::LoggerDotNetworkThruput()
	: m_eos(0)
{
	LogHandler::getInstance()->addLogger(this, "network_transfer");
}

/**
 * Stores the Eos pointer
 */
void LoggerDotNetworkThruput::eos_simulate_start(void *eos)
{
	m_eos = (Eos *)eos;
}

/**
 * Writes log to disk
 */
void LoggerDotNetworkThruput::eos_simulate_stop(void *eos)
{
	fstream fout;
	openFile(&fout, "networkthruput.end.dot");
	outputNetoworkDot((Eos *) eos, fout);
	fout.close();
}

/**
 * @todo Describe this...
 */
void LoggerDotNetworkThruput::network_transfer(void *sender, void *transfer, unsigned int bytesSent)
{
	/// @test Is this necessary? -> Do a find on the item, if not found set it to zero.
	std::map<Node *, std::map<Node *, double> >::iterator findNode = m_nodeTransfers.find((Node *)sender);
	if(findNode != m_nodeTransfers.end()){
		std::map<Node *, double>::iterator findTransfer = (*findNode).second.find((Node *)(((Transfer *)transfer)->m_destination));
		if(findTransfer != (*findNode).second.end()){

		}
		else{
			m_nodeTransfers[(Node *)sender][(Node *)(((Transfer *)transfer)->m_destination)] = 0;
		}
	}
	else{
		m_nodeTransfers[(Node *)sender][(Node *)(((Transfer *)transfer)->m_destination)] = 0;
	}

	m_nodeTransfers[(Node *)sender][(Node *)(((Transfer *)transfer)->m_destination)] += ((double)bytesSent/Clock::m_secondsPerTimeStep/1024);
	//cout << m_nodeTransfers[(Node *)sender][(Node *)(((Transfer *)transfer)->m_destination)] << "\t" << ((double)bytesSent/Clock::m_secondsPerTimeStep/1024) << "\t" << m_nodeTransfers[(Node *)sender][(Node *)(((Transfer *)transfer)->m_destination)] - ((double)bytesSent/Clock::m_secondsPerTimeStep/1024) << endl;
}

/**
 * Writes the dot-files to disk
 * @param eos Pointer to the simulator where we will get the node list
 * @param outputFile The fstream that the data should be written to.
 */
void LoggerDotNetworkThruput::outputNetoworkDot(Eos *eos, fstream &outputFile)
{
	/// All nodes that have a busyness-value (in percent) below this will get omitted from display
	const double nodeFilterLimit = 5;
	/// The largest size the nodes will get
	const double nodeSizeMax = 0.5;
	/// The smallest size the nodes will get
	const double nodeSizeMin = 0.05;
	/// What is the lightest color alowed for nodes (setting this to 100 will make the least bussy nodes invisible)
	const int lightestNodeColor = 99;
	/// What is the lightest color alowed for edges (transfer-connections) (setting this to 100 will make the least bussy edges invisible)
	const int lightestEdgeColor = 99;
	/// All edges that have a busyness-value (in percent) above this will get a label showing the transfer
	const int edgeLabelLimit = 100;
	/// All edges that have a busyness-value (in percent) above this and a label will get its label highlited (typical: 75)
	const int edgeLabelHiLiteLimit = 100;
	/// The maximum (sort of) length of an edge (dynamically calculated from busyness and transfer amount) (typixal: 99)
	const double edgeLengthMax = 2;
	/// The minimum length of an edge (dynamically calculated from busyness and transfer amount)
	const double edgeLengthMin = 0.2;
	/// All edges that has a color above this value will be invisible.
	const int edgeInvisiLimit = 97;

	double currentTime = (double)Clock::getInstance()->getTime();
	double maxTransfer = 0;
	double currentTransfer;
	double edgeBusyness, nodeBusyness;
	std::map<Node *, bool> nodeFilter;

	// Create node UL-speed partition limits
	unsigned int ulSpeedLimits[4];
	for(int i=0; i<4; i++){
		ulSpeedLimits[i] = Distributions::getInstance()->bandwidthUp(RAND_MAX/4)*(i+1);
	}

	// Write dot header
	outputFile << "graph G {" << endl;

	// Specify global node style
	outputFile << "node [label=\"\", height=0.1 ,width=0.1, color=gray" << lightestNodeColor << ", style=filled];";

	// Output Node specc and find busiest transfer
	for(std::map<Node *, std::map<Node *, double> >::iterator transfers = m_nodeTransfers.begin(); transfers != m_nodeTransfers.end(); transfers++){
		currentTransfer = 0;
		for(std::map<Node *, double>::iterator transfer = (*transfers).second.begin(); transfer != (*transfers).second.end(); transfer++){
			currentTransfer += (*transfer).second;
			if((*transfer).second > maxTransfer){
				maxTransfer = (*transfer).second;
			}
		}
		nodeBusyness = currentTransfer / (((*transfers).first->getBandwidthUp() * currentTime )/Clock::m_secondsPerTimeStep/1024);
		// Check if the node is busy enough
		if((nodeBusyness*100) > nodeFilterLimit){
			outputFile << "n" << (*transfers).first << " [color=gray" << lightestNodeColor - (int)(lightestNodeColor * nodeBusyness);
			outputFile << setiosflags(ios::fixed);
			double currentNodeDiameter = nodeSizeMin+(nodeBusyness*(nodeSizeMax-nodeSizeMin));
			outputFile << ", height=" << currentNodeDiameter << ", width=" << currentNodeDiameter;
			outputFile << resetiosflags(ios::fixed);
			// Check node UL-speed and set "shape=something" accordingly. Nice shapes are: diamond, pentagon, hexagon, septagon, octagon
			unsigned int nodeBandwitdth = (*transfers).first->getBandwidthUp();
			outputFile << ", shape=";
			if(nodeBandwitdth < ulSpeedLimits[0]){
				outputFile << "triangle";
			}
			else if(nodeBandwitdth < ulSpeedLimits[1]){
				outputFile << "diamond";
			}
			else if(nodeBandwitdth < ulSpeedLimits[2]){
				outputFile << "hexagon";
			}
			else{
				outputFile << "octagon";
			}
			outputFile << "];" << endl;
			nodeFilter[(*transfers).first] = true;
		}
	}

	// Write transfer dot data
	// Go thru all transmitting nodes
	for(std::map<Node *, std::map<Node *, double> >::iterator transfers = m_nodeTransfers.begin(); transfers != m_nodeTransfers.end(); transfers++){

		// Check if the sending node is busy enough
		std::map<Node *, bool>::iterator findNodeSource = nodeFilter.find((*transfers).first);
		if(findNodeSource != nodeFilter.end()){

			// Go thru all transfers of this node
			for(std::map<Node *, double>::iterator transfer = (*transfers).second.begin(); transfer != (*transfers).second.end(); transfer++){

				// Check if the receiveing node is busy enough
				std::map<Node *, bool>::iterator findNodeDest = nodeFilter.find((*transfer).first);
				if(findNodeDest != nodeFilter.end()){

					// Calculate the busyness for this transfer
					edgeBusyness = (*transfer).second / (((*transfers).first->getBandwidthUp() * currentTime )/Clock::m_secondsPerTimeStep/1024);

					outputFile << "n" << (*transfers).first << " -- " << "n" << (*transfer).first << " [dir=";
					outputFile << "forward";

					// Bold "fast" connections
					//if((*transfers).first->getBandwidthUp() > Distributions::getInstance()->bandwidthUp(RAND_MAX/2)){
					if(min( (*transfers).first->getBandwidthUp() , (*transfer).first->getBandwidthDown() ) > Distributions::getInstance()->bandwidthDown(RAND_MAX/2)){
						outputFile << ", style=bold";
					}

					int edgeColor = lightestEdgeColor - (int)(lightestEdgeColor * edgeBusyness );
					outputFile << ", color=gray" << edgeColor;

					// Set a label on busy connections
					if((*transfer).second > (maxTransfer/100)*edgeLabelLimit){
						outputFile << ", label=" << (*transfer).second << ", fontcolor=";
						if((*transfer).second >= (maxTransfer/100)*edgeLabelHiLiteLimit){
							outputFile << "firebrick";
						}
						else{
							outputFile << "gray";
						}
					}

					// Set edge length
					outputFile << setiosflags(ios::fixed);
					outputFile << ", len=" << (edgeLengthMin + ( (1-edgeBusyness) * (edgeLengthMax*(1-((*transfer).second/maxTransfer))) ));
					outputFile << resetiosflags(ios::fixed);

					if(edgeInvisiLimit < edgeColor){
						outputFile << ", style=invis";
					}

					outputFile << "];" << endl;
				}
			}
		}
	}

	// Limit the running time when generating the image
	outputFile << "maxiter=1000";
	// Make the edges a little longer so that you don't end upp with a knot of blackness...
	//outputFile << ", overlap=scale";
	// Paint the edges first so that the nodes wont be hidden behind a web of edges
	//outputFile << ", outputorder=edgesfirst";
	outputFile << ";" << endl;

	// Write dot footer
	outputFile << "}" << endl;
}
